import React, { Component, PureComponent } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import Styles from './index.less'

// check all of elements is valid
function checkErrIsAllFixed(arr) {
  return arr.every((item) => item === '')
}
// iterator of create react element
function iteratorReactElementCreator() {
  this.iteratorList = [],
  this.pointer = null
}
iteratorReactElementCreator.prototype.start = function() {
  for ( var i = 0, fn; fn = this.iteratorList[i++]; ) {
    var reactElement = fn.apply(this.pointer, arguments)
    if (reactElement !== false) {
      return reactElement
    }
  }
}
iteratorReactElementCreator.prototype.fixPointer = function() {
  this.pointer = arguments[0]
}
iteratorReactElementCreator.prototype.add = function() {
  this.iteratorList = arguments
}
function typeButton(child, index) {
  if (child.type === Button) {
    const _self = this
    const isRequiredAllHasValue = Object.keys(this.state).filter((item) => this.requiredElements.includes(item)).every((item) => this.state[item]!=='')
    return React.cloneElement(child, {
      onClick: () => {
        console.log(_self.state, this.state.errMsgs)
      },
      disabled: !checkErrIsAllFixed(this.state.errMsgs) || !isRequiredAllHasValue
    })
  }
  return false
}
class Hoc extends PureComponent {

}

function typeInputCheck(child, index) {
  if (child.type === InputCheck) {
    const _self = this
    return React.cloneElement(child, {
      onChange: (value) => {
        _self.setState({
          [child.props.name]: value
        }, () => {
          const errMsgs = this.state.errMsgs
          for (let i = 0, rule; rule = child.props.rules[i++]; ) {
            if (!rule) {
              return
            }
            let errMsg
            if (rule.rule === 'isTheSame') {
              errMsg = strategies['isTheSame'](_self.state[rule.refName], value, rule.msg)
            }
            else {
              errMsg = strategies[rule.rule](value, rule.msg)
            }
            if (errMsg) {
              _self.setState({
                errMsgs: errMsgs.map((item, i) => {
                  if (i === index) {
                    return errMsg
                  }
                  return item
                })
              })
              return
            }
            _self.setState({
              errMsgs: errMsgs.map((item, i) => {
                if (i === index) {
                  return ''
                }
                return item
              })
            })
          }
        })
      },
      errMsg: this.state.errMsgs[index]
    })
  }
  return false
}
export function extendInputCheck({wrapOuterAfter}) {
  return (WrappedComponent) => {
    class Hoc extends PureComponent {
      static displayName = 'InputCheck'
      render() {
        return (
          <div>
            {WrappedComponent}
            {wrapOuterAfter}
          </div>
        )
      }
    }
    return <Hoc/>
  }
}
// some verification strategies
const strategies = {
  isNonEmpty: function(value, errorMsg) {
    if (value === '') {
      return errorMsg
    }
  },
  isRegularPsw: function(value, errorMsg) {
    if (!/\w{6,12}/.test(value)) {
      return errorMsg
    }
  },
  isEmail: function(value, errorMsg) {
    if (!/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/.test(value)) {
      return errorMsg
    }
  },
  isMobile: function(value, errorMsg) {
    if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
      return errorMsg
    }
  },
  isTheSame: function(referenceValue, value, errorMsg) {
    if (referenceValue !== value) {
      return errorMsg
    }
  }
}
export class InputCheck extends PureComponent {
  static propTypes = {
    placeholder: PropTypes.string,
    rules: PropTypes.arrayOf(PropTypes.object),
    name: PropTypes.string,
    required: PropTypes.bool,
  }
  static defaultProps = {
    type: 'text',
    required: true,
  }
  constructor() {
    super()
    this.state = {
      errMsg: ''
    }
    this.onChange = this.onChange.bind(this)
  }
  onChange(e) {
    this.props.onChange(e.target.value)
  }
  render() {
    const { placeholder, errMsg, type } = this.props
    return (
      <div className={Styles.inputCheck}>
        <input type={type} className={Styles.inputText} placeholder={placeholder} 
               onChange={this.onChange}
               />
        {this.props.rules ? (
          <p className={Styles.errMsg}>{errMsg}</p>
        ) : null}
      </div>
    )
  }
}
export class Button extends Component {
  constructor(props) {
    super(props)
    this.onClick = this.onClick.bind(this)
  }
  onClick() {
    this.props.onClick()
  }
  render() {
    const { className, type, disabled } = this.props
    const btnClass = className ? className : classNames({[Styles[`btn-${type}`]]: true, [Styles.disabled]: disabled})
    return (
      <button disabled={disabled} onClick={this.onClick} className={btnClass}>{this.props.children}</button>
    )
  }
}
//所有输入类型的类组件,包括input, select等
const InputArr = [InputCheck]
export class FormContainer extends Component {
  static propTypes = {
    children: PropTypes.arrayOf(PropTypes.element).isRequired,
    onChange: PropTypes.func,
    className: PropTypes.string,
  }
  constructor(props) {
    super(props)
    let childList = React.Children.toArray(props.children)
    let inputElements = childList.filter((item) => InputArr.includes(item.type))
    let formValue = inputElements.reduce((accumulator, currentValue, currentIndex, array) => {
      return currentIndex === 1 ? Object.assign({},{[accumulator.props.name]: ''}, {[currentValue.props.name]: ''}) : Object.assign({}, accumulator, {[currentValue.props.name]: ''})
    })
    this.requiredElements = inputElements.filter((item) => item.props.required).map((item) => item.props.name)
    this.state = {...formValue, errMsgs: new Array(React.Children.count(inputElements)).fill('')}
    this.renderChildren = this.renderChildren.bind(this)
  }
  renderChildren() {
    const iterator = new iteratorReactElementCreator()
    iterator.add(typeButton, typeInputCheck)
    iterator.fixPointer(this)
    return React.Children.map(this.props.children, (child, index) => {
      console.log(child)
      return iterator.start(child, index)
    }  
    )
  }
  render() {
    return (
      <div className={this.props.className}>
        {this.renderChildren()}
      </div>
    )
  }
}
